package com.uam.core.lk.utils;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.geotools.geometry.jts.GeometryBuilder;
import org.locationtech.jts.geom.*;
import org.locationtech.jts.linearref.LengthIndexedLine;
import org.locationtech.jts.math.Vector2D;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;

/**
 * 原jts里的很多方法都是基于2d计算的，重写了部分方法按照3d计算
 *
 * @author heguanglong
 * @version 1.0
 * @since JDK1.8
 */
@Slf4j
public class GeoTools {
    private static GeometryFactory geometryFactory = new GeometryFactory();

    /**
     * 方法描述:  折线等分
     *
     * @param p0  起点
     * @param p1  终点
     * @param num 等分数
     * @throws
     * @Return {@link List<  Coordinate >}
     * @author heguanglong
     * @date 2022年02月22日 14:11:09
     */
    public static List<Coordinate> polylineDivide(Coordinate p0, Coordinate p1, int num) {
        double factor = 1.0D / num;
        int points = num - 1;
        List<Coordinate> coordinates = new ArrayList<>(points);
        for (int i = 0; i < points; i++) {
            coordinates.add(pointAlong(p0, p1, factor * (i + 1)));
        }
        return coordinates;
    }


    /**
     * 方法描述: 线段比例点坐标计算
     *
     * @param p0
     * @param p1
     * @param factor
     * @return {@link Coordinate}
     * @throws
     * @author heguanglong
     * @date 2022年02月23日 13:49:52
     */
    public static Coordinate pointAlong(Coordinate p0, Coordinate p1, double factor) {
        Coordinate coord = new Coordinate();
        coord.x = p0.x + factor * (p1.x - p0.x);
        coord.y = p0.y + factor * (p1.y - p0.y);
        coord.z = p0.y + factor * (p1.z - p0.z);
        return coord;
    }

    /**
     * 方法描述:  判断点是否在线段上
     *
     * @param p  给定点
     * @param p0 线段开始点坐标
     * @param p1 线段结束点坐标
     * @Return {@link boolean}
     * @author heguanglong
     * @date 2022年02月20日 16:51:49
     */
    public static boolean onLineSegment(Coordinate p, Coordinate p0, Coordinate p1) {
        double total = distance(p0, p1);
        double toStart = distance(p, p0);
        double toEnd = distance(p, p1);
        double diff = total - toEnd - toStart;
        return diff == 0;
    }

    /**
     * 方法描述:  判断点是否在一条直线上
     *
     * @param p  给定点
     * @param p0 线段开始点坐标
     * @param p1 线段结束点坐标
     * @Return {@link boolean}
     * @author heguanglong
     * @date 2022年02月22日 16:51:49
     */
    public static boolean onLine(Coordinate p, Coordinate p0, Coordinate p1) {
        double lsLen = distance(p0, p1);
        double toStart = distance(p, p0);
        double toEnd = distance(p, p1);
        double diff = lsLen - toEnd - toStart;
        if (diff == 0) {
            return true;
        }
        diff = toEnd - toStart - lsLen;
        if (diff == 0) {
            return true;
        }
        diff = toStart - toEnd - lsLen;
        if (diff == 0) {
            return true;
        }
        return false;
    }


    /**
     * 方法描述: 线段中间点
     *
     * @param p0
     * @param p1
     * @return {@link Coordinate}
     * @throws
     * @author heguanglong
     * @date 2022年02月23日 09:33:01
     */
    public static Coordinate midPoint(Coordinate p0, Coordinate p1) {
        return new Coordinate((p0.x + p1.x) / 2.0D, (p0.y + p1.y) / 2.0D, (p0.z + p1.z) / 2.0D);
    }

    // 计算给定距离和方向上的点的经纬度坐标
    public static Coordinate calculatePointOnLine(double lat1, double lon1, double distance, double bearing) {
        double R = 6371000; // 地球的半径，单位为米

        double lat1Radians = Math.toRadians(lat1);
        double lon1Radians = Math.toRadians(lon1);
        double bearingRadians = Math.toRadians(bearing);

        double lat2 = Math.asin(Math.sin(lat1Radians) * Math.cos(distance / R) +
                Math.cos(lat1Radians) * Math.sin(distance / R) * Math.cos(bearingRadians));
        double lon2 = lon1Radians + Math.atan2(Math.sin(bearingRadians) * Math.sin(distance / R) * Math.cos(lat1Radians),
                Math.cos(distance / R) - Math.sin(lat1Radians) * Math.sin(lat2));

        // 将弧度转换为度数
        lat2 = Math.toDegrees(lat2);
        lon2 = Math.toDegrees(lon2);

        return new Coordinate(lon2, lat2);
    }

    // 在线段上获取指定距离的点
    public static Coordinate getPointOnLine(Coordinate start, Coordinate end, double rate) {
        LineString line = geometryFactory.createLineString(new Coordinate[]{end, start});
        LengthIndexedLine indexedLine = new LengthIndexedLine(line);
        Coordinate point = indexedLine.extractPoint(indexedLine.getEndIndex() * rate);
       return point;

        // 输出点的坐标
//        System.out.println("Extracted Point Coordinate: " + extractedCoord);

//        List<Coordinate> coordinates=new ArrayList<>(16);
//
//        com.mapbox.geojson.Point point = com.mapbox.geojson.Point.fromLngLat(start.x, start.y);
//        com.mapbox.geojson.Point point90= TurfMeasurement.destination(point, distance, 90, TurfConstants.UNIT_METERS);
//        coordinates.add(new Coordinate(point90.longitude(),point90.latitude()));
//
//        com.mapbox.geojson.Point point901= TurfMeasurement.destination(point, distance, -90, TurfConstants.UNIT_METERS);
//        coordinates.add(new Coordinate(point901.longitude(),point901.latitude()));
//
//        com.mapbox.geojson.Point point180= TurfMeasurement.destination(point, distance, -180, TurfConstants.UNIT_METERS);
//        coordinates.add(new Coordinate(point180.longitude(),point180.latitude()));
//
//        com.mapbox.geojson.Point point270= TurfMeasurement.destination(point, distance, 180, TurfConstants.UNIT_METERS);
//        coordinates.add(new Coordinate(point270.longitude(),point270.latitude()));
//
//        Coordinate coordinate=null;
//
//        List<Coordinate> data=new ArrayList<>();
//        for(int i=0;i<coordinates.size();i++){
////           if( onLineSegment(start,end,coordinates.get(i))){
//               data.add(coordinates.get(i));
////           }
//        }
//
//        if(data.size() ==1){
//            return data.get(0);
//        }
//
//        List<TempVo> tempVo=new ArrayList<>();
//        if(data.size()>1){
//
//            for(int i=0;i<data.size();i++) {
//                Coordinate tempDistance= data.get(i);
//                double distance2=distance(tempDistance,end);
//                tempVo.add(new TempVo(i,distance2));
//            }
//        }
//        //降序
//        List<TempVo>  v1 = tempVo.stream().sorted(Comparator.comparing(TempVo::getDistance).reversed()).collect(Collectors.toList());
//
//        v1.forEach(a->{
//          log.info (a.getIndex()+" "+a.getDistance());
//        });
//
//        if(tempVo.size()>0) {
//            return  data.get(v1.get(0).getIndex());
//        }
//
//        return data.get(0);

    }

    public static double calculateAngleBetweenPoints(Coordinate point1, Coordinate point2) {
        // 创建向量
        Vector2D vector1 = new Vector2D(point1.x, point1.y);
        Vector2D vector2 = new Vector2D(point2.x, point2.y);

        // 计算夹角
        double angle = vector1.angleTo(vector2);

        // 将弧度转换为度数
        angle = Math.toDegrees(angle);

        return angle;
    }

    public static void main(String[] args) {
        Coordinate[] line = new Coordinate[2];
        Coordinate coordinate1 = new Coordinate(112.991936, 28.251097);
        Coordinate coordinate2 = new Coordinate(112.991999, 28.249999);

//        line[0] = coordinate1;
//        line[1] = coordinate2;
//        double tempSum = RegionUtil.computeDistance(coordinate1.x, coordinate1.y, coordinate2.x, coordinate2.y);
//        System.out.println(tempSum);
////    Coordinate point = GeoTools.getPointOnLine(coordinate1,coordinate2, 33.0001);
//////        System.out.println(tempSum);
////////        for(int i=0;i<=100;i++) {
//////            Point point=GeoTools.getPointOnLine(line, i);
////        System.out.println(tempSum+"  " + point.x+"," +point.y);
////////        }
////
//        GeometryFactory geometryFactory = new GeometryFactory();
//        Coordinate[] coordinates = new Coordinate[]{
//                new Coordinate(112.991936, 28.251097),
//
//                new Coordinate(112.991999, 28.249999)
//        };
//        LineString linee = geometryFactory.createLineString(coordinates);
//
//        System.out.println(tempSum);
//        // 指定距离
//        double distance =1000*(tempSum-30); // 距离线段起点的距离
//
//        // 提取指定距离处的点
//        Coordinate point = extractPointAlongLine(linee, distance);
//        System.out.println(distance);
//        // 输出提取的点坐标
//        System.out.println("提取的点坐标：" + point);

        // 定义线段的两个端点
        Coordinate p0 = new Coordinate(112.991936, 28.251097);
        Coordinate p1 = new Coordinate(112.991999, 28.249999);

        // 创建LineSegment对象
        LineSegment lineSeg = new LineSegment(p1,p0);

        // 在线段的内部选取一个点，例如在线段长度的2/3处
        double fraction = 2.0 / 3.0;
        Coordinate extractedCoord = lineSeg.pointAlong(fraction);
        JSONArray jsonArray= new JSONArray(100);

        JSONArray jsonArray1=new JSONArray();
        jsonArray1.add(p0.x);
        jsonArray1.add(p0.y);


        JSONObject jsonObject =new JSONObject(12);
        jsonObject.put("position",jsonArray1);
        jsonObject.put("name","1");

        jsonArray.add(jsonObject);

        JSONArray jsonArray2=new JSONArray();
        jsonArray2.add(extractedCoord.x);
        jsonArray2.add(extractedCoord.y);

        JSONObject jsonObject2 =new JSONObject(12);
        jsonObject2.put("position",jsonArray2);
        jsonObject2.put("name","2");
        jsonArray.add(jsonObject2);

        JSONArray jsonArray3=new JSONArray();
        jsonArray3.add(p1.x);
        jsonArray3.add(p1.y);


        JSONObject jsonObject3 =new JSONObject(12);
        jsonObject3.put("position",jsonArray3);
        jsonObject3.put("name","3");

        jsonArray.add(jsonObject3);

        System.out.println(JSONArray.toJSONString(jsonArray));
        // 输出点的坐标
        System.out.println("Extracted Point Coordinate: " + extractedCoord);
    }

    public static Coordinate extractPointAlongLine(LineString line, double distance) {
        // 创建线性参考对象
        LengthIndexedLine indexedLine = new LengthIndexedLine(line);

        // 提取指定距离处的点坐标
        return indexedLine.extractPoint(distance);
    }

    /**
     * 方法描述:  点与点的距离
     *
     * @param p0
     * @param p1
     * @return {@link double}
     * @throws
     * @author heguanglong
     * @date 2022年02月23日 09:30:03
     */
    public static double distance3D(Coordinate p0, Coordinate p1) {
        return p0.distance3D(p1);
    }


    /**
     * 方法描述:  点与点的距离
     *
     * @param p0
     * @param p1
     * @return {@link double}
     * @throws
     * @author heguanglong
     * @date 2022年02月23日 09:30:03
     */
    public static double distance(Coordinate p0, Coordinate p1) {
        return p0.distance(p1);
    }

    /**
     * 方法描述:  转换点坐标
     *
     * @param x
     * @param y
     * @param z
     * @return {@link Coordinate}
     * @throws
     * @author heguanglong
     * @date 2022年02月22日 10:35:28
     */
    public static Coordinate getCoordinate(BigDecimal x, BigDecimal y, BigDecimal z) {
        if (x == null) {
            x = BigDecimal.ZERO;
        }
        if (y == null) {
            y = BigDecimal.ZERO;
        }
        if (z == null) {
            z = BigDecimal.ZERO;
        }
        Coordinate coordinate = new Coordinate(x.doubleValue(), y.doubleValue(), z.doubleValue());
        return coordinate;
    }

    /**
     * 方法描述: 线段角度 (基于xy的二维角度)
     *
     * @param p0
     * @param p1
     * @return {@link double}
     * @throws
     * @author heguanglong
     * @date 2022年02月24日 14:14:33
     */
    public static double angle(Coordinate p0, Coordinate p1) {
        LineSegment ls = new LineSegment(p0, p1);
        return ls.angle();
    }

    /**
     * 方法描述: 线段角度 (基于xy的二维角度和基于xz的二维角度)
     *
     * @param p0
     * @param p1
     * @return {@link double}
     * @throws
     * @author heguanglong
     * @date 2022年02月24日 14:14:33
     */
    public static double[] angle3D(Coordinate p0, Coordinate p1) {
        double[] angles = new double[2];
        angles[0] = Math.atan2(p1.y - p0.y, p1.x - p0.x);
        double c = p0.distance(p1);
        angles[1] = Math.atan2(p1.z - p0.z, c);
        return angles;
    }

    /**
     * 方法描述: 二维移动点
     *
     * @param p
     * @param angle
     * @param distance
     * @return {@link Coordinate}
     * @throws
     * @author heguanglong
     * @date 2022年02月24日 14:14:49
     */
    public static Coordinate movePoint(Coordinate p, double angle, double distance) {
        double sin = Math.sin(angle);
        double a = distance * sin;
        double cos = Math.cos(angle);
        double b = distance * cos;
        return new Coordinate(p.x + b, p.y + a, 0);
    }


    /**
     * 方法描述: 三维移动点
     *
     * @param p
     * @param angles
     * @param distance
     * @return {@link Coordinate}
     * @throws
     * @author heguanglong
     * @date 2022年02月24日 14:14:49
     */
    public static Coordinate movePoint3D(Coordinate p, double[] angles, double distance) {
        double sin1 = Math.sin(angles[1]);
        double d = distance * sin1;
        double cos1 = Math.cos(angles[1]);
        double c = distance * cos1;
        double sin0 = Math.sin(angles[0]);
        double a = c * sin0;
        double cos0 = Math.cos(angles[0]);
        double b = c * cos0;
        return new Coordinate(p.x + b, p.y + a, p.z + d);
    }


    /**
     * 方法描述: 获取点离多段线最近的线及投影点
     *
     * @param
     * @return LineSegment
     * @auther heguanglong
     * @date 2022年02月28日 14:09:10
     */
    public static LineSegment nearestLineSegment(Coordinate p, List<LineSegment> lines) {
        double distance = Double.MAX_VALUE;
        LineSegment result = null;
        for (LineSegment ls : lines) {
            double distance2 = distance(p, ls.p0, ls.p1);
            distance = Double.min(distance, distance2);
            if (distance2 <= distance) {
                result = ls;
            }
        }
        return result;
    }

    /**
     * 方法描述: 点到线的距离
     *
     * @param p
     * @param p0
     * @param p1
     * @return {@link double}
     * @throws
     * @author heguanglong
     * @date 2022年02月23日 09:21:13
     */
    public static double distance3D(Coordinate p, Coordinate p0, Coordinate p1) {
        GeometryBuilder builder = new GeometryBuilder();
        Point point = builder.pointZ(p.x, p.y, p.z);
        LineString ls = builder.lineStringZ(p0.x, p0.y, p0.z, p1.x, p1.y, p1.z);
        return point.distance(ls);
    }

    /**
     * 方法描述: 点到线的距离
     *
     * @param p
     * @param p0
     * @param p1
     * @return {@link double}
     * @throws
     * @author heguanglong
     * @date 2022年02月23日 09:21:13
     */
    public static double distance(Coordinate p, Coordinate p0, Coordinate p1) {
        LineSegment ls = new LineSegment(p0, p1);
        return ls.distance(p);
    }

    /**
     * 方法描述:  点与线段上的最近点计算
     *
     * @param p  给定点
     * @param p0 起始点
     * @param p1 结束点
     * @throws
     * @Return {@link Coordinate}
     * @author heguanglong
     * @date 2021年08月03日 18:01:42
     */
    public static Coordinate closestPoint(Coordinate p, Coordinate p0, Coordinate p1) {
        double factor = projectionFactor(p, p0, p1);
        if (factor > 0.0D && factor < 1.0D) {
            return project(p, p0, p1);
        } else {
            double dist0 = p0.distance3D(p);
            double dist1 = p1.distance3D(p);
            return dist0 < dist1 ? p0 : p1;
        }
    }

    /**
     * 方法描述: 点在线上的投影点
     *
     * @param p
     * @param p0
     * @param p1
     * @return {@link Coordinate}
     * @throws
     * @author heguanglong
     * @date 2022年02月23日 09:25:31
     */
    public static Coordinate project(Coordinate p, Coordinate p0, Coordinate p1) {
        if (!p.equals3D(p0) && !p.equals3D(p1)) {
            double r = projectionFactor(p, p0, p1);
            Coordinate coord = new Coordinate();
            coord.x = p0.x + r * (p1.x - p0.x);
            coord.y = p0.y + r * (p1.y - p0.y);
            coord.z = p0.z + r * (p1.z - p0.z);
            return coord;
        } else {
            return new Coordinate(p);
        }
    }

    /**
     * 方法描述:  投影系数
     *
     * @param p
     * @param p0
     * @param p1
     * @return {@link double}
     * @throws
     * @author heguanglong
     * @date 2022年02月23日 09:26:33
     */
    private static double projectionFactor(Coordinate p, Coordinate p0, Coordinate p1) {
        if (p.equals3D(p0)) {
            return 0.0D;
        } else if (p.equals3D(p1)) {
            return 1.0D;
        } else {
            double dx = p1.x - p0.x;
            double dy = p1.y - p0.y;
            double dz = p1.z - p0.z;
            double len = dx * dx + dy * dy + dz * dz;
            if (Double.compare(len, 0) == 0) {
                return 0;
            } else {
                double r = ((p.x - p0.x) * dx + (p.y - p0.y) * dy + (p.z - p0.z) * dz) / len;
                return r;
            }
        }
    }


    /**
     * 方法描述:  转相对坐标
     *
     * @param p     坐标点
     * @param baseP 基准点
     * @return {@link Coordinate}
     * @throws
     * @author heguanglong
     * @date 2022年04月22日 10:05:31
     */
    private static Coordinate relativeCoordinate(Coordinate p, Coordinate baseP) {
        double x = p.getX() - baseP.getX();
        double y = p.getY() - baseP.getY();
        double z = p.getZ() - baseP.getZ();
        return new Coordinate(x, y, z);
    }

    /**
     * 方法描述: 转巴比伦坐标
     *
     * @param p
     * @param baseP
     * @return {@link Coordinate}
     * @throws
     * @author heguanglong
     * @date 2022年04月22日 10:06:03
     */
    private static Coordinate babylonCoordinate(Coordinate p, Coordinate baseP) {
        Coordinate p0 = relativeCoordinate(p, baseP);
        p0.setX(-p0.getX());
        p0.setY(p0.getZ());
        p0.setZ(-p0.getY());
        return p0;
    }
}
